use crate::bitboard_tables::*;
use crate::board::Pos;
use crate::color::Color;
use crate::piece::Piece;
use std::convert::TryInto;
use std::ops::BitAnd;
use std::ops::BitAndAssign;
use std::ops::BitOr;
use std::ops::BitOrAssign;
use std::ops::Not;

#[repr(align(16))]
#[derive(Clone, Copy)]
pub struct BitBoard {
    bits: u128,
}

fn bits_from_pos_index(index: u8) -> u128 {
    let index: usize = index.into();
    POS_TO_BITBOARD_TABLE[index]
}

impl BitBoard {
    // 空的位棋盘
    pub fn new() -> Self {
        BitBoard { bits: 0 }
    }

    pub fn is_empty(self) -> bool {
        self.bits == 0
    }

    pub fn is_not_empty(self) -> bool {
        self.bits != 0
    }

    fn lower64(self) -> u64 {
        ((self.bits << 64) >> 64).try_into().unwrap()
    }

    fn upper64(self) -> u64 {
        (self.bits >> 64).try_into().unwrap()
    }

    pub fn pawn_pos_bitboard(color: Color) -> BitBoard {
        BitBoard {
            bits: PAWN_POS_TABLE[color.color_index()],
        }
    }

    pub fn king_pos_bitboard() -> BitBoard {
        BitBoard {
            bits: KING_POS_TABLE,
        }
    }

    pub fn guard_pos_bitboard() -> BitBoard {
        BitBoard {
            bits: GUARD_POS_TABLE,
        }
    }

    pub fn minister_pos_bitboard() -> BitBoard {
        BitBoard {
            bits: MINISTER_POS_TABLE,
        }
    }

    pub fn pawn_attack_bitboard(pos: Pos, color: Color) -> BitBoard {
        BitBoard {
            bits: PAWN_ATTACK_TABLE[color.color_index()][pos.pos_index()],
        }
    }

    pub fn king_attack_bitboard(pos: Pos) -> BitBoard {
        BitBoard {
            bits: KING_ATTACK_TABLE[pos.pos_index()],
        }
    }

    pub fn guard_attack_bitboard(pos: Pos) -> BitBoard {
        BitBoard {
            bits: GUARD_ATTACK_TABLE[pos.pos_index()],
        }
    }

    pub fn minister_attack_bitboard(pos: Pos, bitboard: BitBoard) -> BitBoard {
        let magic: u64 = MINISTER_MAGIC_TABLE[pos.pos_index()];
        let mask: u128 = MINISTER_MASK_TABLE[pos.pos_index()];
        let bitboard = bitboard & BitBoard { bits: mask };
        let index: usize = ((magic.wrapping_mul(bitboard.lower64())
            | magic.wrapping_mul(bitboard.upper64()))
            >> (64 - 4))
            .try_into()
            .unwrap();
        let bits = MINISTER_MOVES_TABLE[index + pos.pos_index() * 16];
        BitBoard { bits }
    }

    pub fn knight_attack_bitboard(pos: Pos, bitboard: BitBoard) -> BitBoard {
        let magic: u64 = KNIGHT_MAGIC_TABLE[pos.pos_index()];
        let mask: u128 = KNIGHT_MASK_TABLE[pos.pos_index()];
        let bitboard = bitboard & BitBoard { bits: mask };
        let index: usize = ((magic.wrapping_mul(bitboard.lower64())
            | magic.wrapping_mul(bitboard.upper64()))
            >> (64 - 4))
            .try_into()
            .unwrap();
        let bits = KNIGHT_MOVES_TABLE[index + pos.pos_index() * 16];
        BitBoard { bits }
    }

    // 根据棋盘初始化各个棋子的位棋盘
    pub fn init_from_piece_array_pred(
        piece_array: &[Option<Piece>; 90],
        pred: Box<dyn Fn(Piece) -> bool>,
    ) -> Self {
        let mut bits = 0;
        for (i, option_piece) in piece_array.iter().enumerate() {
            if let Some(p) = option_piece {
                if pred(*p) {
                    bits |= bits_from_pos_index(i.try_into().unwrap());
                }
            }
        }
        Self { bits }
    }

    pub fn mutate_self_return_pos(&mut self) -> Option<Pos> {
        if self.is_empty() {
            return None;
        }
        let pos = Pos::from_pos_index(self.bits.trailing_zeros().try_into().unwrap());
        self.bits &= self.bits - 1;
        Some(pos)
    }
}

impl Iterator for BitBoard {
    type Item = Pos;

    fn next(&mut self) -> Option<Pos> {
        if self.bits == 0 {
            return None;
        }
        let pos = Pos::from_pos_index(self.bits.trailing_zeros().try_into().unwrap());
        self.bits &= self.bits - 1;
        Some(pos)
    }
}

// 位棋盘的与或非操作

impl BitOr for BitBoard {
    type Output = Self;

    fn bitor(self, rhs: Self) -> Self {
        let bits = self.bits | rhs.bits;
        Self { bits }
    }
}

impl BitOrAssign for BitBoard {
    fn bitor_assign(&mut self, rhs: Self) {
        self.bits |= rhs.bits;
    }
}

impl BitAnd for BitBoard {
    type Output = Self;

    fn bitand(self, rhs: Self) -> Self {
        let bits = self.bits & rhs.bits;
        Self { bits }
    }
}

impl BitAndAssign for BitBoard {
    fn bitand_assign(&mut self, rhs: Self) {
        self.bits &= rhs.bits;
    }
}

impl Not for BitBoard {
    type Output = Self;

    fn not(self) -> Self {
        let bits = !self.bits;
        Self { bits }
    }
}
